package net.ufrog.common.web.app;

import net.ufrog.common.Logger;
import net.ufrog.common.Messages;
import net.ufrog.common.app.App;
import net.ufrog.common.app.AppUser;
import net.ufrog.common.app.Validation;
import net.ufrog.common.exception.ServiceException;
import net.ufrog.common.utils.Calendars;
import net.ufrog.common.utils.Codecs;
import net.ufrog.common.utils.Streams;
import net.ufrog.common.utils.Strings;
import net.ufrog.common.web.RequestParam;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Locale;

/**
 * 互联网应用信息
 *
 * @author ultrafrog
 * @version 1.0, 2013-10-01
 * @since 1.0
 */
public class WebApp extends App {

    private static final String SESSION_USER			= "session_user";
    private static final String SESSION_TOKEN			= "session_token";

    private static final String CONF_PATH_COMMON        = "path.common";
    private static final String CONF_PATH_HOST			= "path.host";
    private static final String CONF_PATH_RESOURCE		= "path.resource";
    private static final String CONF_PATH_IMAGE			= "path.image";
    private static final String CONF_PATH_JAVASCRIPT	= "path.javascript";
    private static final String CONF_PATH_STYLESHEET	= "path.stylesheet";
    private static final String CONF_PATH_THEME			= "path.theme";

    private static String version;
    private static String tempPath;
    private static String contextPath;
    private static String commonPath;
    private static String homePath;
    private static String resourcePath;
    private static String imagePath;
    private static String javascriptPath;
    private static String stylesheetPath;
    private static String themeName;

    protected HttpServletRequest request;
    private HttpServletResponse response;

    private Validation validation;
    private RequestParam requestParam;

    /**
     * 构造函数
     *
     * @param request 请求
     * @param response 响应
     */
    public WebApp(HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.validation = new Validation();
        this.response = response;
    }

    @Override
    public AppUser getUser() {
        return session(SESSION_USER, AppUser.class);
    }

    @Override
    public AppUser setUser(AppUser user) {
        return session(SESSION_USER, user);
    }

    @Override
    public String getToken() {
        String token = session(SESSION_TOKEN, String.class);
        return (token == null) ? updateToken() : token;
    }

    @Override
    public String updateToken() {
        return session(SESSION_TOKEN, Codecs.uuid());
    }

    @Override
    public Locale getLocale() {
        return request.getLocale();
    }

    @Override
    public Validation getValidation() {
        return validation;
    }

    @Override
    public String getMessage(String key, Object... args) {
        return Messages.get(getLocale(), key, args);
    }

    @Override
    public String toString() {
        return getPath();
    }

    /**
     * @param key 键值
     * @param obj 内容
     * @return 内容
     */
    public <T> T session(String key, T obj) {
        if (obj == null) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                session.removeAttribute(key);
            }
            return null;
        }
        request.getSession().setAttribute(key, obj);
        return obj;
    }

    /**
     * @param key 键值
     * @return 内容
     */
    public Object session(String key) {
        return request.getSession().getAttribute(key);
    }

    /**
     * @param key 键值
     * @param requiredType 请求类型
     * @return 内容
     */
    public <T> T session(String key, Class<T> requiredType) {
        return requiredType.cast(session(key));
    }

    /**
     * @param key 键值
     * @param value 内容
     * @param duration 有效期
     */
    public void cookie(String key, String value, String duration) {
        Cookie cookie = new Cookie(key, value);
        cookie.setMaxAge(Calendars.parseDuration(duration));
        response.addCookie(cookie);
    }

    /**
     * @param key 键值
     * @param value 内容
     */
    public void cookie(String key, String value) {
        cookie(key, value, "14d");
    }

    /**
     * @param key 键值
     * @return 内容
     */
    public String cookie(String key) {
        if (request.getCookies() == null) return null;
        for (Cookie cookie: request.getCookies()) {
            if (Strings.equals(cookie.getName(), key)) {
                return cookie.getValue();
            }
        }
        return null;
    }

    /**
     * 读取版本号
     *
     * @return 版本号
     */
    public String getVersion() {
        return version;
    }

    /**
     * 读取临时路径
     *
     * @return 临时路径
     */
    public String getTempPath() {
        return tempPath;
    }

    /**
     * 读取应用路径
     *
     * @return 应用路径
     */
    public String getPath() {
        return contextPath;
    }

    /**
     * 读取公共路径
     *
     * @return 公共路径
     */
    public String getCommon() {
        return commonPath;
    }

    /**
     * 读取资源路径
     *
     * @return 资源路径
     */
    public String getResource() {
        return resourcePath;
    }

    /**
     * 读取图片路径
     *
     * @return 图片路径
     */
    public String getImg() {
        return imagePath;
    }

    /**
     * 读取脚本路径
     *
     * @return 脚本路径
     */
    public String getJs() {
        return javascriptPath;
    }

    /**
     * 读取样式路径
     *
     * @return 样式路径
     */
    public String getCss() {
        return stylesheetPath;
    }

    /**
     * 读取主题
     *
     * @return 主题
     */
    public String getTheme() {
        return themeName;
    }

    /**
     * 读取主题图片路径
     *
     * @return 主题图片路径
     */
    public String getThemeImg() {
        return String.format("%s/theme/%s/images", resourcePath, themeName);
    }

    /**
     * 读取主题脚本路径
     *
     * @return
     */
    public String getThemeJs() {
        return String.format("%s/theme/%s/javascripts", resourcePath, themeName);
    }

    /**
     * 读取主题样式路径
     *
     * @return 主题样式路径
     */
    public String getThemeCss() {
        return String.format("%s/theme/%s/stylesheets", resourcePath, themeName);
    }

    /**
     * 读取当前地址
     *
     * @return 当前地址
     */
    public String getHome() {
        if (homePath == null) {
            String scheme = request.getScheme();
            Integer port = request.getServerPort();
            StringBuilder url = Strings.builder(scheme);

            url.append("://");
            url.append(request.getServerName());
            if (!(port == 80 && Strings.equals("http", scheme)) && !(port == 443 && Strings.equals("https", scheme))) {
                url.append(":").append(port);
            }
            url.append(request.getContextPath());
            homePath = url.toString();
        }
        return homePath;
    }

    /**
     * 读取请求参数
     *
     * @return 请求参数
     */
    public RequestParam getRequestParam() {
        if (requestParam == null) {
            requestParam = new RequestParam(request);
        }
        return requestParam;
    }

    /**
     * 读取请求体内容
     *
     * @return 请求体字节数组
     */
    public byte[] getBody() {
        if (request != null) {
            try {
                return Streams.toByteArray(request.getInputStream());
            } catch (IOException e) {
                throw new ServiceException(e.getMessage(), e);
            }
        }
        return new byte[1];
    }

    /**
     * 读取请求体字符串
     *
     * @param charset 字符集
     * @return 请求体字符串
     */
    public String getBodyText(String charset) {
        return getBodyText(Charset.forName(charset));
    }

    /**
     * 读取请求体字符串
     *
     * @param charset 字符集
     * @return 请求体字符串
     */
    public String getBodyText(Charset charset) {
        return new String(getBody(), charset);
    }

    /**
     * 读取请求体字符串
     *
     * @return 请求体字符串
     */
    public String getBodyText() {
        return getBodyText("utf-8");
    }

    /**
     * 初始化
     *
     * @param context 上下文
     */
    protected static void initialize(ServletContext context) {
        version = Codecs.uuid();
        tempPath = String.valueOf(context.getAttribute("javax.servlet.context.tempdir"));
        contextPath = context.getContextPath();
        commonPath = config(CONF_PATH_COMMON);
        resourcePath = config(CONF_PATH_HOST, contextPath) + config(CONF_PATH_RESOURCE, "/resources");
        imagePath = config(CONF_PATH_HOST, contextPath) + config(CONF_PATH_IMAGE, "/resources/images");
        javascriptPath = config(CONF_PATH_HOST, contextPath) + config(CONF_PATH_JAVASCRIPT, "/resources/javascripts");
        stylesheetPath = config(CONF_PATH_HOST, contextPath) + config(CONF_PATH_STYLESHEET, "/resources/stylesheets");
        themeName = config(CONF_PATH_THEME);

        Logger.info("initialize temp path: %s", tempPath);
        Logger.info("initialize context path: %s", contextPath);
        Logger.info("initialize common path: %s", commonPath);
        Logger.info("initialize resource path: %s", resourcePath);
        Logger.info("initialize image path: %s", imagePath);
        Logger.info("initialize javascript path: %s", javascriptPath);
        Logger.info("initialize stylesheet path: %s", stylesheetPath);
        Logger.info("initialize theme name: %s", themeName);
    }

    /**
     * 创建实例
     *
     * @param request 请求
     * @param response 响应
     * @return 当前线程应用实例
     */
    static App create(HttpServletRequest request, HttpServletResponse response) {
        current(new WebApp(request, response));
        return current();
    }

    /**
     * 判断是否资源请求
     *
     * @param req 请求
     * @return 判断结果
     */
    public static boolean resource(ServletRequest req) {
        return HttpServletRequest.class.cast(req).getRequestURI().startsWith(resourcePath);
    }
}
